<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            list-style: none;
        }

        .box {
            width: 1000px;
            height: 800px;
            margin: 30px auto;
            display: flex;
            border: 1px solid;
        }

        #myCanvas {
            margin: auto;
            border: 1px solid;
        }
    </style>
</head>

<body>

    <div class="box">
        <canvas id="myCanvas" height="750" width="950" tabindex="333"></canvas>
    </div>
    <div id="txt"></div>
</body>
<script>
    const txt = document.getElementById("txt")
    let ids = null
    /** @type { HTMLCanvasElement } */
    const myCanvas = document.getElementById('myCanvas');
    const ctx = myCanvas.getContext('2d');
    // 设计箭头样式参数 ->
    const arrowParams = {
        startPoint: {
            x: 300,
            y: 400
        },
        arrowBodyLength: 170,    // 箭身长度
        xAngel: 20, // 与 x 轴的正向夹角（实际角度，不是弧度）
        arrowHeadLength: 20,    // 箭头长度
    }
    // 画线函数(极坐标版)
    function drawLine(ctx, arrowParam, degree = 0, lineStyle = '#000') {
        const startPointX = arrowParam.startPoint.x
        const startPointY = arrowParam.startPoint.y
        ctx.save();
        ctx.beginPath();

        ctx.moveTo(startPointX, startPointY)
        ctx.lineTo(startPointX + arrowParam.arrowBodyLength * Math.cos(Math.PI / 180 * degree), startPointY + arrowParam.arrowBodyLength * Math.sin(Math.PI / 180 * degree))
        ctx.strokeStyle = lineStyle
        ctx.stroke()

        ctx.closePath();
        ctx.restore();

        return {
            startPoint: {
                x: startPointX,
                y: startPointY
            },
            endPoint: {
                x: startPointX + arrowParam.arrowBodyLength * Math.cos(Math.PI / 180 * degree),
                y: startPointY + arrowParam.arrowBodyLength * Math.sin(Math.PI / 180 * degree)
            }
        }
    }

    /**
     * @description: 画箭头的第二种方式（极坐标方法）
     * @param {*} ctx   context
     * @param {*} arrowParam    箭头参数
     * @param {number} xAngel    与 x 轴所夹角度，默认是 arrowParam.xAngel
     * @return {*}
     */
    function drawArrow(ctx, arrowParam, xAngel = arrowParam.xAngel) {
        if (!xAngel) {
            xAngel = 0
        }
        let pointInfo = drawLine(ctx, arrowParam, xAngel)
        let yUp = {
            startPoint: pointInfo.endPoint,
            arrowBodyLength: arrowParam.arrowHeadLength,    // 箭身长度
            xAngel: xAngel + 45 + 180, // 与 x 轴的正向夹角（实际角度，不是弧度）    // 算正向角度
        }
        let yDown = {
            startPoint: pointInfo.endPoint,
            arrowBodyLength: arrowParam.arrowHeadLength,    // 箭身长度
            xAngel: xAngel - 45 + 180, // 与 x 轴的正向夹角（实际角度，不是弧度）
        }
        ctx.save();
        ctx.beginPath();
        drawLine(ctx, yUp, yUp.xAngel)
        drawLine(ctx, yDown, yDown.xAngel)
        ctx.closePath();
        ctx.restore();
    }

    drawArrow(ctx, arrowParams)
    drawArrow(ctx, arrowParams, 0)
    drawArrow(ctx, arrowParams, 100)
    drawArrow(ctx, arrowParams, 200)
    drawArrow(ctx, arrowParams, 300)


    myCanvas.onmousemove = (e) => {
        console.log(e);
        txt.innerHTML = `
            鼠标相对位置：（${e.offsetX},${e.offsetY}）；
            鼠标与线段起点连线与x轴所成角度：${Math.atan2(e.offsetY - arrowParams.startPoint.y, e.offsetX - arrowParams.startPoint.x) * 180 / Math.PI}
        `
        animationRotate(ctx, e.offsetX, e.offsetY, arrowParams)
    }

    function animationRotate(ctx, targetX, targetY, arrowParams) {
        ctx.clearRect(0, 0, myCanvas.width, myCanvas.height)

        if (ids) {
            window.cancelAnimationFrame(ids)
        }

        if (targetX == arrowParams.startPoint.x && targetY == arrowParams.startPoint.y) {
            drawArrow(ctx, arrowParams)
            window.cancelAnimationFrame(ids)
            ids = null
            return false;
        }


        let angel = Math.atan2(targetY - arrowParams.startPoint.y, targetX - arrowParams.startPoint.x) * 180 / Math.PI
        drawArrow(ctx, arrowParams, angel)

        ids = window.requestAnimationFrame(() => animationRotate(ctx, targetX, targetY, arrowParams));
    }

</script>

</html>